﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using SpeechLib;
using System.Media;
using System.Net.Sockets;
using HomeAutomation.util;
using HomeAutomation.actions;
using HomeAutomation.modem;


namespace HomeAutomation
{
    public partial class Form1 : Form
    {
        private static bool TEST_MODE = true;

        private const int timeBetweenX10AndInsteon = 800;
        private const int timeBetweenX10AndX10 = 1600;
        private const int timeBetweenReceives = 100;
        private const int SUN_DOWN = 16;
        private const int BED_TIME = 22;
        private static bool exitingProgram = false;

        private static int lastDayAllOffFired = 0;
        private static int lastDayTurnedOnMakLight = 0;



        private Modem modem;
        private enum Command { On, Off };
        //private PlmNetworkX10.HouseContext downStairsChime;
        private RequestList requestList = new RequestList();

        private const String DATE_FORMAT = "dd-HH:mm:ss";
        
        private const int x10Retries = 4;

        static Form1()
        {
            
        }

        public Form1()
        {
            InitializeComponent();
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);

            modem = ModemFactory(TEST_MODE);
            

            // Setup a timer on the GUI thread to call the Receive loop
            var timer = new System.Windows.Forms.Timer();//new DispatcherTimer();
            timer.Interval = timeBetweenReceives;
            timer.Tick += new EventHandler((s, args) =>
            {
                plm.Receive();

                if (requestList.hasNextReady())
                {
                    process(requestList.removeReady());
                }else{
                    DateTime now = DateTime.Now;
                    if (lastDayAllOffFired != now.DayOfYear)
                    {
                        log("day has changed, firing all lights off event soon");
                        lastDayAllOffFired = now.DayOfYear;
                        //just incase there could be a race condition:
                        now = now.AddSeconds(30);
                        requestList.add(now, new DeviceAction(Device.FRONT_SENSOR, HomeAutomation.Command.off));
                        //buffer between two event so close to eachother
                        now = now.AddSeconds(30);
                        requestList.add(now, new DeviceAction(Device.BACK_SENSOR, HomeAutomation.Command.off));
                    }
                }
            });

            plm.Network.StandardMessageReceived += new StandardMessageReceivedHandler((s,e) =>
            {
                try
                {
                    Device device = Device.getDevice_throwsException(e.PeerId.ToString());
                    HomeAutomation.Command command = HomeAutomation.Command.getCommand_throwsException(e.Description.ToString());
                    DeviceAction deviceAction = new DeviceAction(device, command);
                    process(deviceAction);
                }
                catch (Exception ex)
                {
                    log("ERROR\n" +
                    ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" +
                    ex.Message + "\n" +
                    ex.StackTrace + "\n" +
                    "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
                }


            });

            plm.Network.X10.CommandReceived += new X10CommandReceivedHandler((s, e) =>
            {
               log("X10 Command Received: House Code " + e.HouseCode
                    + ", Command: " + e.Command.ToString());
            });

            plm.OnError += new EventHandler((s, e) =>
            {
                log("ERROR\n" + 
                    ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + 
                    plm.Exception.Message + "\n" +
                    //plm.Exception.Data.ToString() + "\n" +
                    //plm.Exception.HelpLink.ToString() + "\n" +
                    //plm.Exception.InnerException.ToString() + "\n" +
                    //plm.Exception.Source.ToString() + "\n" +
                    //plm.Exception.TargetSite.ToString() + "\n" +
                    //plm.Exception.ToString() + "\n" +
                    //s.ToString() + "\n" +
                    //e.ToString() + "\n" +
                    "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
            });

            timer.Start();
            
            //uncomment for camera listener:
            //new Thread(ListenForCamera).Start();
        }

        private void process(DeviceAction deviceAction)
        {
            
            Device device = deviceAction.device;
            HomeAutomation.Command command = deviceAction.command;
            
            //**string peerId = e.PeerId.ToString();

            log("Received Command: " + command.ToString()/*e.Description*/
                 + ", from " + device.ToString()//getNiceName(peerId) 
                 //+ "," + e.MessageType.ToString()
                 );


            if (Device.ALL_SENSORS.Contains(device))
            {
                if (HomeAutomation.Command.on.Equals(command))
                {
                    if (Device.FRONT_AND_BACK_SENSORS.Contains(device))
                    {
                        //x10Send(Device.MAKAY_LIGHT, X10Command.On);
                        //Thread.Sleep(timeBetweenX10AndInsteon);
                        x10Send(Device.INSIDE_LIGHT, X10Command.On);
                        Thread.Sleep(timeBetweenX10AndX10);
                        x10Send(Device.downstairsChime, X10Command.On);
                        Thread.Sleep(timeBetweenX10AndInsteon);
                    }

                    if (Device.FRONT_AND_DRIVEWAY_SENSORS.Contains(device))
                    {
                        frontLight(Command.On);
                        if (Device.FRONT_SENSOR.Equals(device))
                        {
                            say("the front door");
                        }
                    }
                    else if (Device.BACK_SENSOR.Equals(device))
                    {
                        backLight(Command.On);
                        say("backyard");
                    }
                    else
                    {
                        log("not sure what to do with last command");
                    }
                }
                else if (HomeAutomation.Command.off.Equals(command))
                {
                    x10Send(Device.INSIDE_LIGHT, X10Command.Off);
                    Thread.Sleep(timeBetweenX10AndInsteon);
                    
                    int curHour = DateTime.Now.TimeOfDay.Hours;
                    if (curHour >= SUN_DOWN && curHour < BED_TIME)
                    {
                        log("not turning off lights as it's that time of day -- hour ("+curHour+") is within: [" + SUN_DOWN + "," + BED_TIME + ")");
                        return;
                    }

                    //x10Send(Device.MAKAY_LIGHT, X10Command.Off);
                    //Thread.Sleep(timeBetweenX10AndInsteon);

                    if (Device.FRONT_AND_DRIVEWAY_SENSORS.Contains(device))
                    {
                        frontLight(Command.Off);
                    }
                    else if (Device.BACK_SENSOR.Equals(device))
                    {
                        backLight(Command.Off);
                    }
                    else
                    {
                        log("not sure what to do with last command");
                    }
                }
            }
        }

        private void ListenForCamera()
        {
            log("listenForCamera() is disabled");

            //log("starting listenForCamera()");
            //while (true)
            //{
            //    try
            //    {
            //        TcpListener serverSocket = new TcpListener(61234);
                    
            //        serverSocket.Start();
            //        log("Listeneing for Camera Event");
            //        IAsyncResult asynResult = serverSocket.BeginAcceptTcpClient(new AsyncCallback(ListenForCameraAcceptSocket), serverSocket);// .AcceptTcpClient();

            //        while (!asynResult.IsCompleted)
            //        {
            //            if (exitingProgram)
            //            {
            //                return;
            //            }

            //            //busy wait
            //            Thread.Sleep(1000);
            //        }

            //        serverSocket.Stop();
            //    }
            //    catch (Exception ex)
            //    {
            //        log(ex.ToString());
            //    }


            //}
        }

        private void ListenForCameraAcceptSocket(IAsyncResult ar)
        {
            log("Got Camera Event");

            try
            {
                TcpListener listener = (TcpListener)ar.AsyncState;
                TcpClient clientSocket = listener.EndAcceptTcpClient(ar);
                
                NetworkStream networkStream = clientSocket.GetStream();
                byte[] bytesFrom = new byte[10025];
                networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
                string dataFromClient = System.Text.Encoding.Unicode.GetString(bytesFrom);//System.Text.Encoding.ASCII.GetString(bytesFrom);
                //dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
                log("Data from client");// + dataFromClient);

                //turn lights on
                DateTime now = DateTime.Now;
                log("got camera event at " + now.ToString(DATE_FORMAT) + " simulating front sensor on");
                DeviceAction frontSensorOn = new DeviceAction(Device.FRONT_SENSOR, HomeAutomation.Command.on);
                requestList.add(now, frontSensorOn);

                //turn lights off
                DateTime inOneMinute = DateTime.Now.AddMinutes(1);
                log("got camera event at " + now.ToString(DATE_FORMAT) + " simulating front sensor off at " + inOneMinute.ToString(DATE_FORMAT));
                DeviceAction frontSensorOff = new DeviceAction(Device.FRONT_SENSOR, HomeAutomation.Command.off);
                requestList.add(inOneMinute, frontSensorOff);

                //string serverResponse = "Server response " + Convert.ToString(requestCount);
                //Byte[] sendBytes = Encoding.ASCII.GetBytes(serverResponse);
                //networkStream.Write(sendBytes, 0, sendBytes.Length);
                //networkStream.Flush();
                //Console.WriteLine(" >> " + serverResponse);


                clientSocket.Close();
            }
            catch (Exception ex)
            {
                log(ex.ToString());
            }
        }

        //private string getNiceName(String id)
        //{
        //    if (insteonDevices.ContainsKey(id))
        //    {
        //        return insteonDevices[id].name();
        //    }
        //    else
        //    {
        //        return id + " (not in our known list)";
        //    }
        //}

        private void say(string text)
        {
            SpVoice voice = new SpVoice();
            //voice.Voice = voice.GetVoices("Name=LH Michael", "Language=409").Item(0);
            voice.Volume = 100;
            //voice.Rate = //-10 to 10
            //voice.
            voice.Speak(text, SpeechVoiceSpeakFlags.SVSFlagsAsync);//, SpeechVoiceSpeakFlags.SVSFDefault);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            x10Send(Device.downstairsChime, X10Command.On);
        }

        

        private void button2_Click(object sender, EventArgs e)
        {
            //SpVoice voice = new SpVoice();
            //voice.Voice = voice.GetVoices("Name=LH Michael", "Language=409").Item(0);
            //voice.Speak(txttexttospeech.Text, SpeechVoiceSpeakFlags.SVSFDefault);

            say(txttexttospeech.Text);

            //SpeechSynthesizer reader = new SpeechSynthesizer();


            //downStairsChime.Command(X10Command.Off);
            //plm.Network.X10.House("E").Unit(5).Command(X10Command.On);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            process(new DeviceAction(Device.FRONT_SENSOR, HomeAutomation.Command.on));
        }

        private void button4_Click(object sender, EventArgs e)
        {
            process(new DeviceAction(Device.FRONT_SENSOR, HomeAutomation.Command.off));

        }

        private void log(String info)
        {
            if (logT.InvokeRequired)
            {
                this.BeginInvoke(new Action<string>(log), new object[] { info });
            }
            else
            {
                DateTime now = DateTime.Now;
                logT.Text = now.ToString(DATE_FORMAT) + " - " + info + "\n" + logT.Text;
            }
        }

        private Boolean frontLight(Command c)
        {
            return light(c, Device.FRONT_LIGHTS.id());//"18.c7.8f");
        }

        private Boolean backLight(Command c)
        {
            return light(c, Device.BACK_LIGHTS.id());//"17.fd.1e");
        }

        private Boolean light(Command c, string location)
        {
            DeviceBase device;
            if (plm.Network.TryConnectToDevice(location, out device))
            {
                var lightingControl = device as DimmableLightingControl;
                switch (c)
                {
                    case Command.On:
                        lightingControl.TurnOn();
                        break;
                    case Command.Off:
                        lightingControl.TurnOff();
                        break;
                    default:
                        log("Don't know what to do with " + c + " in " + location);
                        return false;
                }
                return true;
            }
            return false;
        }



        private void x10Send(X10Device device, X10Command command)
        {
            plm.Network.X10.House(device.house()).Unit(device.unit()).Command(command);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            exitingProgram = true;
        }
   

   
    }
}
